package so.ifl.mybatis.plugins;

import java.sql.Connection;
import java.util.Properties;

import org.apache.ibatis.executor.statement.PreparedStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.ibatis3.plugin.Dialect;
import org.springframework.orm.ibatis3.plugin.PropertiesHelper;

import so.ifl.commons.tools.ReflectUtil;

/**
 * @author atyuan.lu
 * <mailto:atyuan.lu@gmail.com>
 * @since 2011-4-7
 **/
@Intercepts( {@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class DiclectStatementHandlerInterceptor implements Interceptor {
	
	private Logger _logger = LoggerFactory.getLogger(this.getClass());
	
	private Dialect dialect;


	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		 RoutingStatementHandler routingStatementHandler = (RoutingStatementHandler)invocation.getTarget();
	        PreparedStatementHandler preparedStatementHandler = (PreparedStatementHandler)ReflectUtil.getFieldValue(routingStatementHandler, "delegate");
	        RowBounds rowBounds = (RowBounds)ReflectUtil.getFieldValue(preparedStatementHandler, "rowBounds");
	       
	        if (rowBounds.getLimit() > 0
	                && rowBounds.getLimit() < RowBounds.NO_ROW_LIMIT)
	        {
	            BoundSql boundSql = routingStatementHandler.getBoundSql();
	            String sql = boundSql.getSql();
	           
	            sql = dialect.getLimitString(sql, rowBounds.getOffset(), rowBounds.getLimit());	           
	            ReflectUtil.setFieldValue(boundSql, "sql", sql);
	        }
	        return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
		String dialectClass = new PropertiesHelper(properties).getRequiredString("dialectClass");
		try {
			dialect = (Dialect)Class.forName(dialectClass).newInstance();
		} catch (Exception e) {
			throw new RuntimeException("cannot create dialect instance by dialectClass:"+dialectClass,e);
		} 
		
		_logger.info("DiclectStatementHandlerInterceptor Set dialect=" + dialectClass);

	}

}
